module MetaWeblogStructs
  class Article < ActionWebService::Struct
    member :description,        :string
    member :title,              :string
    member :postid,             :string
    member :url,                :string
    member :link,               :string
    member :permaLink,          :string
    member :categories,         [:string]
    member :mt_text_more,       :string
    member :mt_excerpt,         :string
    member :mt_keywords,        :string
    member :mt_allow_comments,  :int
    member :mt_allow_pings,     :int
    member :mt_convert_breaks,  :string
    member :mt_tb_ping_urls,    [:string]
    member :dateCreated,        :time
  end

  class MediaObject < ActionWebService::Struct
    member :bits, :string
    member :name, :string
    member :type, :string
  end

  class Url < ActionWebService::Struct
    member :url, :string
  end
end

class MetaWeblogApi < ActionWebService::API::Base
  inflect_names false

  api_method :getCategories,
    :expects => [ {:blogid => :string}, {:username => :string}, {:password => :string} ],
    :returns => [[:string]]

  api_method :getPost,
    :expects => [ {:postid => :string}, {:username => :string}, {:password => :string} ],
    :returns => [MetaWeblogStructs::Article]

  api_method :getRecentPosts,
    :expects => [ {:blogid => :string}, {:username => :string}, {:password => :string}, {:numberOfPosts => :int} ],
    :returns => [[MetaWeblogStructs::Article]]

  api_method :deletePost,
    :expects => [ {:appkey => :string}, {:postid => :string}, {:username => :string}, {:password => :string}, {:publish => :int} ],
    :returns => [:bool]

  api_method :editPost,
    :expects => [ {:postid => :string}, {:username => :string}, {:password => :string}, {:struct => MetaWeblogStructs::Article}, {:publish => :int} ],
    :returns => [:bool]

  api_method :newPost,
    :expects => [ {:blogid => :string}, {:username => :string}, {:password => :string}, {:struct => MetaWeblogStructs::Article}, {:publish => :int} ],
    :returns => [:string]

  api_method :newMediaObject,
    :expects => [ {:blogid => :string}, {:username => :string}, {:password => :string}, {:data => MetaWeblogStructs::MediaObject} ],
    :returns => [MetaWeblogStructs::Url]
end


class MetaWeblogService < ActionWebService::Base
  web_service_api MetaWeblogApi
  before_invocation :authenticate

  def authenticate(name, args)
    method = self.class.web_service_api.api_methods[name]
    begin
      h = method.expects_to_hash(args)
      raise "Invalid login" unless User.authenticate(:login => h[:username], :password => h[:password])
    rescue NoMethodError
      username, password = method[:expects].index(:username=>String), method[:expects].index(:password=>String)
      raise "Invalid login" unless User.authenticate(:login => h[:username], :password => h[:password])
    end
  end
  
  def initialize(controller)
    @controller = controller
  end
  
  def getCategories(blogid, username, password)
    Tag.find(:all).collect { |t| t.name }
  end

  def getPost(postid, username, password)
    article = Post.find(postid)
    article_dto_from(article)
  end

  def getRecentPosts(blogid, username, password, numberOfPosts)
    Post.find(:all, :order => "published_at DESC", :limit => numberOfPosts).collect{ |c| article_dto_from(c) }
  end

  def newPost(blogid, username, password, struct, publish)
    post = Post.new
    post.raw_body       = struct['description'] || ''
    post.title          = struct['title'] || ''
    post.published      = publish
    post.published_at   = struct['dateCreated'].to_time.getlocal rescue Time.now
    post.tag_string     = struct['categories'].join(" ") if struct['categories']
    post.user_id        = User.find_by_login(username).id
    post.comments_disabled_after = APP_SETTINGS[:disable_post_comments_after]

    if !post.save
      raise post.errors.full_messages * ", "
    end
    post.id.to_s
  end

  def deletePost(appkey, postid, username, password, publish)
    post = Post.find(postid)
    post.destroy
    true
  end

  def editPost(postid, username, password, struct, publish)
    post = Post.find(postid)
    post.raw_body      = struct['description'] || ''
    post.title         = struct['title'] || ''
    post.published     = publish
    post.published_at  = struct['dateCreated'].to_time.getlocal unless struct['dateCreated'].blank?
    post.tag_string     = struct['categories'].join(" ") if struct['categories']
    
    if !post.save
      raise post.errors.full_messages * ", "
    end
    true
  end

  def newMediaObject(blogid, username, password, data)
    asset = Asset.new({:filename => data['name'], :content_type => data['type'], :temp_data => data['bits']})    
    asset.save
    MetaWeblogStructs::Url.new("url" => asset.public_filename)
  end

  def article_dto_from(article)
    MetaWeblogStructs::Article.new(
      :description       => article.body,
      :title             => article.title,
      :postid            => article.id.to_s,
      :url               => "",
      :link              => "",
      :permaLink         => "",
      :categories        => article.tags,
      :mt_text_more      => "",
      :mt_excerpt        => "",
      :mt_keywords       => [],
      :mt_allow_comments => 1,
      :mt_allow_pings    => 1,
      :mt_convert_breaks => "",
      :mt_tb_ping_urls   => [],
      :dateCreated       => (article.published_at.getutc.to_formatted_s(:db) rescue "")
      )
  end
end
